use super::*;

/// JavaScript Date wrapper
#[derive(Clone, Copy)]
pub struct Date<'val> {
	pub(crate) env: &'val Env,
	pub(crate) inner: sys::napi_value,
}

impl<'val> Date<'val> {
	// Constructor methods
	/// Creates a new Date with the given milliseconds
	pub fn new<'env>(env: &'env Env, init: f64) -> Result<'static, Date<'env>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_create_date(env.raw(), init, &mut ret) }.result("napi_create_date")?;
		Ok(Date { env, inner: ret })
	}

	/// Creates a Date with the current time
	pub fn now<'env>(env: &'env Env) -> Result<'static, Date<'env>> {
		let now = std::time::SystemTime::now()
			.duration_since(std::time::UNIX_EPOCH)
			.map_err(|_| Status::invalid_arg("time before UNIX epoch"))?
			.as_millis() as f64;
		Date::new(env, now)
	}

	/// Creates a Date from a timestamp
	pub fn from_timestamp<'env>(env: &'env Env, timestamp: i64) -> Result<'static, Date<'env>> {
		Self::new(env, timestamp as f64)
	}

	/// Creates a Date from milliseconds
	pub fn from_millis<'env>(env: &'env Env, millis: f64) -> Result<'static, Date<'env>> {
		Self::new(env, millis)
	}

	/// Converts to a generic Value
	pub fn v(&'_ self) -> Value<'val> {
		Value::new(self.env, self.inner)
	}

	// Conversion methods
	/// Gets the milliseconds value of the date
	pub fn get(&self) -> Result<'static, f64> {
		let mut ret = 0.0;
		unsafe { sys::napi_get_date_value(self.env.raw(), self.inner, &mut ret) }.result("napi_get_date_value")?;
		Ok(ret)
	}

	/// Gets the timestamp of the date
	pub fn timestamp(&self) -> Result<'static, i64> {
		self.get().map(|millis| millis as i64)
	}

	/// Gets the milliseconds of the date
	pub fn millis(&self) -> Result<'static, f64> {
		self.get()
	}

	/// Checks if the date is valid
	pub fn is_valid(&self) -> Result<'static, bool> {
		let millis = self.get()?;
		Ok(millis.is_finite() && millis >= 0.0)
	}

	/// Converts to std::time::SystemTime
	pub fn to_std_datetime(&self) -> Result<'static, std::time::SystemTime> {
		let millis = self.get()?;
		let duration = std::time::Duration::from_millis(millis as u64);
		Ok(std::time::UNIX_EPOCH + duration)
	}

	// Date arithmetic methods
	/// Adds milliseconds to the date
	pub fn add_millis(&self, millis: f64) -> Result<'static, Self> {
		let current = self.get()?;
		Self::new(self.env, current + millis)
	}

	/// Adds seconds to the date
	pub fn add_seconds(&self, seconds: f64) -> Result<'static, Self> {
		self.add_millis(seconds * 1000.0)
	}

	/// Adds minutes to the date
	pub fn add_minutes(&self, minutes: f64) -> Result<'static, Self> {
		self.add_seconds(minutes * 60.0)
	}

	/// Adds hours to the date
	pub fn add_hours(&self, hours: f64) -> Result<'static, Self> {
		self.add_minutes(hours * 60.0)
	}

	/// Adds days to the date
	pub fn add_days(&self, days: f64) -> Result<'static, Self> {
		self.add_hours(days * 24.0)
	}

	// Date comparison methods
	/// Calculates the difference between two dates in milliseconds
	pub fn difference<'other>(&self, other: &Date<'other>) -> Result<'static, f64> {
		let self_millis = self.get()?;
		let other_millis = other.get()?;
		Ok(self_millis - other_millis)
	}

	/// Checks if two dates are equal (within 1 millisecond)
	pub fn equals<'other>(&self, other: &Date<'other>) -> Result<'static, bool> {
		let diff = self.difference(other)?;
		Ok(diff.abs() < 1.0) // 1毫秒内的差异视为相等
	}

	/// Checks if this date is before another date
	pub fn before<'other>(&self, other: &Date<'other>) -> Result<'static, bool> {
		let diff = self.difference(other)?;
		Ok(diff < 0.0)
	}

	/// Checks if this date is after another date
	pub fn after<'other>(&self, other: &Date<'other>) -> Result<'static, bool> {
		let diff = self.difference(other)?;
		Ok(diff > 0.0)
	}
}

impl<'val> FromNapi<'val> for Date<'val> {
	/// Converts from Value to Date
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		let is = v.is_date()?;
		if is {
			Ok(Date {
				env: v.env,
				inner: v.inner,
			})
		} else {
			Err(Status::invalid_arg("value is not Date type"))
		}
	}
}

impl<'val> IntoNapi for Date<'val> {
	/// Converts Date to Value
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		if self.env.raw() != env.raw() {
			return Err(Status::invalid_arg("self.env not eq arg env"));
		}
		Ok(Value::new(env, self.inner))
	}
}

impl IntoNapi for core::time::Duration {
	/// Converts Duration to Date
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		let millis = self.as_millis() as f64;
		Date::new(env, millis)?.into_napi(env)
	}
}

impl IntoNapi for std::time::SystemTime {
	/// Converts SystemTime to Date
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		let duration = self
			.duration_since(std::time::UNIX_EPOCH)
			.map_err(|_| Status::invalid_arg("time before UNIX epoch"))?;
		duration.into_napi(env)
	}
}
